#include "cserial.h"

#include <QDebug>

#ifdef Q_OS_LINUX
unsigned int CSerial::convBaud(int nBaud) {
    switch (nBaud) {
        default: return 0;
        case 1200:     return B1200;
        case 2400:     return B2400;
        case 4800:     return B4800;
        case 9600:     return B9600;
        case 19200:    return B19200;
        case 38400:    return B38400;
        case 57600:    return B57600;
        case 115200:   return B115200;

    }
    return 0;
}
#endif


CSerial::CSerial()
{
    m_nFD=-1;
    m_bIsOpened=false;
    m_flow=QSerialPort::NoFlowControl;
}

CSerial::~CSerial() {
    close();
}

void CSerial::close() {
    if (!m_bIsOpened) return;

    if (-1 == m_nFD) return;

    ::tcsetattr(m_nFD, TCSANOW, &m_backupTerm);
    ::close(m_nFD);
    m_nFD=-1;
    m_bIsOpened=false;

}

bool CSerial::isOpen() {
    return m_bIsOpened;
}

void CSerial::setPortName(QString szPort) {
    m_qszPort=szPort;
}

void CSerial::setBaudRate(int nBaud) {
    m_nBaud=nBaud;
}

void CSerial::setParity(int p) {
    m_nParity=p;
}

void CSerial::setDataBits(int nBit) {
    m_nDataBits=nBit;
}

void CSerial::setStopBits(int nBit) {
    m_nStopBits=nBit;
}

void CSerial::setFlowControl(QSerialPort::FlowControl f) {
    m_flow=f;
}

bool CSerial::open() {
    bool bRet=false;

#ifdef Q_OS_LINUX
    int nFlags = O_NOCTTY | O_RDWR;

    m_nFD = ::open(m_qszPort.toUtf8().constData(), nFlags, 0666);

    if (-1 == m_nFD) {
        //this->setError();
        return false;
    }

    m_bIsOpened=true;

    if (-1 == ::tcgetattr(m_nFD, &m_backupTerm)) {
        close();
        return false;
    }

    m_currTerm.c_cflag =  convBaud(m_nBaud) |
                        (m_nDataBits == 8 ? CS8 : CS7) |
                        (m_nParity == 0 ? 0 : (m_nParity == 1 ? PARENB | PARODD : PARENB)) |
                        CLOCAL | CREAD;
    if (0 == m_nParity) {
        m_currTerm.c_cflag &= ~(PARENB | PARODD);
    }

    switch (m_nStopBits) {
        default:
        case 1:
            m_currTerm.c_cflag &= ~CSTOPB;
            break;
        case 2:
            m_currTerm.c_cflag |= CSTOPB;
            break;
    }

    switch (m_flow) {
        default:
        case QSerialPort::NoFlowControl:
            break;
        case QSerialPort::HardwareControl:
            //m_currTerm.c_cflag |= CRTSCTS;
            qDebug() << QString().sprintf("flag: 0x%x, 0x%x", m_currTerm.c_cflag, CRTSCTS);
            break;
    }

    m_currTerm.c_iflag = IGNPAR;
    m_currTerm.c_oflag=0;
    m_currTerm.c_lflag=0;
    m_currTerm.c_cc[VMIN]=1;
    m_currTerm.c_cc[VTIME]=0;
    ::cfmakeraw(&m_currTerm);

    ::tcflush(m_nFD, TCIFLUSH);
    ::tcsetattr(m_nFD, TCSANOW, &m_currTerm);
    bRet=true;

#endif
    return bRet;
}

int CSerial::write(char *ptr, int nLeng) {
    int nRet=0;
#ifdef Q_OS_LINUX
    if (-1 == m_nFD) return 0;
    if (0 == nLeng || NULL == ptr) return 0;

    if (QSerialPort::HardwareControl == m_flow) {
        int nStatus;

        ::ioctl(m_nFD, TIOCMGET, &nStatus);
        nStatus |= (TIOCM_RTS | TIOCM_DTR);
        int s=::ioctl(m_nFD, TIOCMSET, &nStatus);

        usleep(100*1000);
        nRet=::write(m_nFD, ptr, nLeng);

        usleep(100*1000);
        nStatus &= ~(TIOCM_RTS | TIOCM_DTR);
        s=::ioctl(m_nFD, TIOCMSET, &nStatus);
    }
    else {
        nRet=::write(m_nFD, ptr, nLeng);
    }
#endif
    qDebug()<< "send data leng: " << nLeng << " sent leng: " << nRet;
    return nRet;
}

int CSerial::read(char *ptr, int nLimitLeng) {
    int nRet=0;
#ifdef Q_OS_LINUX
    if (-1 == m_nFD) return 0;
    if (0 == nLimitLeng || NULL == ptr) return 0;

    if (QSerialPort::HardwareControl == m_flow) {
        int nStatus;

        ::ioctl(m_nFD, TIOCMGET, &nStatus);
        nStatus |= TIOCM_CTS;
        int s=::ioctl(m_nFD, TIOCMSET, &nStatus);
        qDebug() << "set cts: " << s;
        usleep(100*1000);

        nRet=::read(m_nFD, ptr, nLimitLeng);

        nStatus &= ~TIOCM_CTS;
        ::ioctl(m_nFD, TIOCMSET, &nStatus);
    }
    else {
        nRet=::read(m_nFD, ptr, nLimitLeng);
    }
#endif
    qDebug() << "read leng: " << nRet;
    return nRet;
}

void CSerial::flush() {
#ifdef Q_OS_LINUX
    if (-1 == m_nFD) return;


#endif
}

QString CSerial::portName() {
    return m_qszPort;
}
